package study.nosql.redis.support.client;

import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.connection.RedisZSetCommands;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * 1.简单封装
 */
public class RedisV1Client {

    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 根据 RedisTemplate 构造 Client，客户端可有多个 RedisTemplate
     */
    public RedisV1Client(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    /**
     * （★）获取数据类型
     */
    public String type(String key) {
        DataType type;
        try {
            type = redisTemplate.type(key);
        } catch (Exception ex) {
            throw new RuntimeException("type error!", ex);
        }
        return type.code();
    }

    /**
     * （★）设置过期时间
     */
    public boolean expire(String key, long expires) {
        boolean succ;
        try {
            succ = redisTemplate.expire(key, expires, TimeUnit.MILLISECONDS);
        } catch (Exception ex) {
            throw new RuntimeException("expire error!", ex);
        }
        return succ;
    }

    /**
     * （★）key是否存在
     */
    public boolean hasKey(String key) {
        boolean succ;
        try {
            succ = redisTemplate.hasKey(key);
        } catch (Exception ex) {
            throw new RuntimeException("hasKey error!", ex);
        }
        return succ;
    }

    /**
     * （★）获取pattern
     */
    public Set<String> keys(String pattern) {
        Set<String> set;
        try {
            set = redisTemplate.keys(pattern);
        } catch (Exception ex) {
            throw new RuntimeException("keys error!", ex);
        }
        return set;
    }

    /**
     * （★）删除
     */
    public boolean delete(String key) {
        boolean succ;
        try {
            succ = redisTemplate.delete(key);
        } catch (Exception ex) {
            throw new RuntimeException("delete error!", ex);
        }
        return succ;
    }

    /**
     * （★）设置值
     */
    public <V> void set(String key, V value) {
        try {
            redisTemplate.opsForValue()
                    .set(key, value);
        } catch (Exception ex) {
            throw new RuntimeException("[String] set error!", ex);
        }
    }

    public <V> void set(String key, V value, long expires) {
        try {
            redisTemplate.opsForValue()
                    .set(key, value, expires, TimeUnit.MILLISECONDS);
        } catch (Exception ex) {
            throw new RuntimeException("[String] set error!", ex);
        }
    }

    //key不存在时，设置值
    public <V> boolean setIfAbsent(String key, V value) {
        boolean succ;
        try {
            succ = redisTemplate.opsForValue()
                    .setIfAbsent(key, value);
        } catch (Exception ex) {
            throw new RuntimeException("[String] set error!", ex);
        }
        return succ;
    }

    //key不存在时，设置值和过期时间
    public <V> boolean setIfAbsent(String key, V value, long expires) {
        boolean succ;
        try {
            succ = redisTemplate.opsForValue()
                    .setIfAbsent(key, value, expires, TimeUnit.MILLISECONDS);
        } catch (Exception ex) {
            throw new RuntimeException("[String] set error!", ex);
        }
        return succ;
    }

    //key存在时，设置值
    public <V> boolean setIfPresent(String key, V value) {
        boolean succ;
        try {
            succ = redisTemplate.opsForValue()
                    .setIfPresent(key, value);
        } catch (Exception ex) {
            throw new RuntimeException("[String] set error!", ex);
        }
        return succ;
    }

    //key存在时，设置值和过期时间
    public <V> boolean setIfPresent(String key, V value, long expires) {
        boolean succ;
        try {
            succ = redisTemplate.opsForValue().setIfPresent(key, value, expires, TimeUnit.MILLISECONDS);
        } catch (Exception ex) {
            throw new RuntimeException("[String] setIfPresent error!", ex);
        }
        return succ;
    }

    /**
     * （★）获取值
     */
    public <V> V get(String key) {
        V value;
        try {
            value = (V) redisTemplate.opsForValue().get(key);
        } catch (Exception ex) {
            throw new RuntimeException("[String] get error!", ex);
        }
        return value;
    }

    //自增
    public Long incr(String key) {
        Long count;
        try {
            count = redisTemplate.opsForValue().increment(key);
        } catch (Exception ex) {
            throw new RuntimeException("[String] incr error!", ex);
        }
        return count;
    }

    //自减
    public Long decr(String key) {
        Long count;
        try {
            count = redisTemplate.opsForValue().decrement(key);
        } catch (Exception ex) {
            throw new RuntimeException("[String] decr error!", ex);
        }
        return count;
    }

    /**
     * ==============================
     * （★）List类型
     * ==============================
     */
    //头部插入
    public <V> Long lpush(String key, V value) {
        Long size;
        try {
            size = redisTemplate.opsForList().leftPush(key, value);
        } catch (Exception ex) {
            throw new RuntimeException("[List] left push error!", ex);
        }
        return size;
    }

    //尾部插入
    public <V> Long rpush(String key, V value) {
        Long size;
        try {
            size = redisTemplate.opsForList().rightPush(key, value);
        } catch (Exception ex) {
            throw new RuntimeException("[List] right push error!", ex);
        }
        return size;
    }

    //头部弹出
    public <V> V lpop(String key) {
        V value;
        try {
            value = (V) redisTemplate.opsForList().leftPop(key);
        } catch (Exception ex) {
            throw new RuntimeException("[List] right push error!", ex);
        }
        return value;
    }

    //尾部弹出
    public <V> V rpop(String key) {
        V value;
        try {
            value = (V) redisTemplate.opsForList().rightPop(key);
        } catch (Exception ex) {
            throw new RuntimeException("[List] right pop error!", ex);
        }
        return value;
    }

    /**
     * （阻塞式）尾部弹出
     * （1）List中无数据时阻塞；
     * （2）timeout=0表示一直阻塞下去，直到List出现数据;
     */
    public <V> V brpop(String key, long timeout) {
        V value;
        try {
            value = (V) redisTemplate.opsForList().rightPop(key, timeout, TimeUnit.MILLISECONDS);
        } catch (Exception ex) {
            throw new RuntimeException("[List] blocked right pop error!", ex);
        }
        return value;
    }

    //尾部弹出，头部插入
    public <V> V rightPopAndLeftPush(String srcKey, String destKey) {
        V value;
        try {
            value = (V) redisTemplate.opsForList().rightPopAndLeftPush(srcKey, destKey);
        } catch (Exception ex) {
            throw new RuntimeException("[List] blocked right pop error!", ex);
        }
        return value;
    }

    /**
     * ==============================
     * （★）Hash类型
     * ==============================
     */
    //设置值
    public <V> void hput(String key, String hashKey, V value) {
        try {
            redisTemplate.opsForHash().put(key, hashKey, value);
        } catch (Exception ex) {
            throw new RuntimeException("[Hash] put error!", ex);
        }
    }

    //获取值
    public <V> V hget(String key, String hashKey) {
        V value;
        try {
            value = (V) redisTemplate.opsForHash().get(key, hashKey);
        } catch (Exception ex) {
            throw new RuntimeException("[Hash] get error!", ex);
        }
        return value;
    }

    //获取全部值
    public <V> List<V> hgetall(String key) {
        List<V> valueLt;
        try {
            valueLt = (List<V>) redisTemplate.opsForHash().values(key);
        } catch (Exception ex) {
            throw new RuntimeException("[Hash] hgetall error!", ex);
        }
        return valueLt;
    }

    //
    public Long size(String key) {
        Long size;
        try {
            size = redisTemplate.opsForHash().size(key);
        } catch (Exception ex) {
            throw new RuntimeException("[Hash] size error!", ex);
        }
        return size;
    }

    //自增
    public Long hincr(String key, String hashKey, long delta) {
        Long count;
        try {
            count = redisTemplate.opsForHash().increment(key, hashKey, delta);
        } catch (Exception ex) {
            throw new RuntimeException("[Hash] increment error!", ex);
        }
        return count;
    }

    /**
     * ==============================
     * （★）Set类型
     * ==============================
     */
    //设置值
    public Long sadd(String key, String value) {
        Long size;
        try {
            size = redisTemplate.opsForSet().add(key, value);
        } catch (Exception ex) {
            throw new RuntimeException("[Set] add error!", ex);
        }
        return size;
    }

    //获取值
    public <V> V srandomMember(String key) {
        V value;
        try {
            value = (V) redisTemplate.opsForSet().randomMember(key);
        } catch (Exception ex) {
            throw new RuntimeException("[Set] randomMember error!", ex);
        }
        return value;
    }

    //弹出值
    public <V> V spop(String key) {
        V value;
        try {
            value = (V) redisTemplate.opsForSet().pop(key);
        } catch (Exception ex) {
            throw new RuntimeException("[Set] pop error!", ex);
        }
        return value;
    }

    //
//    public static void sscan(String _2_key, long count) {
//        try {
//            ScanOptions options = ScanOptions.scanOptions()
//                    .count(count)
//                    .build();
//            Cursor cursor = ops().scan(_2_key, options);
//        } catch (Exception ex) {
//            throw new RuntimeException("");
//        }
//    }

    //差集
    public <V> Set<V> sdiff(String key, String otherKey) {
        Set<V> value;
        try {
            value = (Set) redisTemplate.opsForSet().difference(key, otherKey);
        } catch (Exception ex) {
            throw new RuntimeException("[Set] difference error!", ex);
        }
        return value;
    }

    //并集
    public <V> Set<V> sunion(String key, String otherKey) {
        Set<V> value;
        try {
            value = (Set) redisTemplate.opsForSet().union(key, otherKey);
        } catch (Exception ex) {
            throw new RuntimeException("[Set] union error!", ex);
        }
        return value;
    }

    //交集
    public <V> Set<V> sinter(String key, String otherKey) {
        Set<V> value;
        try {
            value = (Set) redisTemplate.opsForSet().intersect(key, otherKey);
        } catch (Exception ex) {
            throw new RuntimeException("[Set] intersect error!", ex);
        }
        return value;
    }

    /**
     * ==============================
     * （★）ZSet类型
     * ==============================
     */
    //设置值
    public boolean zadd(String key, String otherKey, double score) {
        boolean succ;
        try {
            succ = redisTemplate.opsForZSet().add(key, otherKey, score);
        } catch (Exception ex) {
            throw new RuntimeException("[ZSet] add error!", ex);
        }
        return succ;
    }

    //增加分数
    public Double zincrScore(String key, String value, double delta) {
        Double score;
        try {
            score = redisTemplate.opsForZSet().incrementScore(key, value, delta);
        } catch (Exception ex) {
            throw new RuntimeException("[ZSet] incrementScore error!", ex);
        }
        return score;
    }

    //获取值
    public <V> Set<V> zrange(String key, Long start, Long end) {
        Set<V> set;
        try {
            set = (Set) redisTemplate.opsForZSet().range(key, start, end);
        } catch (Exception ex) {
            throw new RuntimeException("[ZSet] range error!", ex);
        }
        return set;
    }

    //根据，获取值
    public <V> Set<V> zrangeByLex(String key, Long start, Long end) {
        Set<V> set;
        try {
            set = (Set) redisTemplate.opsForZSet().rangeByLex(key, RedisZSetCommands.Range.range(), RedisZSetCommands.Limit.limit());
        } catch (Exception ex) {
            throw new RuntimeException("[ZSet] rangeByLex error!", ex);
        }
        return set;
    }

    //根据分数，获取值
    public <V> Set<V> zrangeByScore(String key, double min, double max) {
        Set<V> set;
        try {
            set = (Set) redisTemplate.opsForZSet().rangeByScore(key, min, max);
        } catch (Exception ex) {
            throw new RuntimeException("[ZSet] rangeByScore error!", ex);
        }
        return set;
    }

    /**
     * （★）发布
     */
    public void publish(String channel, Object message) {
        try {
            redisTemplate.convertAndSend(channel, message);
        } catch (Exception ex) {
            throw new RuntimeException("[Pub/Sub] publish error!", ex);
        }
    }
}
